package com.ft.statemachine.recipes.persist;

import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.access.StateMachineAccess;
import org.springframework.statemachine.listener.AbstractCompositeListener;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.statemachine.support.LifecycleObjectSupport;
import org.springframework.statemachine.support.StateMachineInterceptorAdapter;
import org.springframework.statemachine.transition.Transition;
import org.springframework.util.Assert;

import java.util.Iterator;
import java.util.List;

/**
 * @author: zhangjianghua
 * @Date: 2018-10-10 14:46
 */
public class PersistEnumStateMachineHandler<S extends Enum<S>, E extends Enum<E>> extends LifecycleObjectSupport {

    private final StateMachine<S , E> stateMachine;
    private final PersistingEnumStateChangeInterceptor interceptor = new PersistingEnumStateChangeInterceptor();
    private final CompositePersistEnumStateChangeListener listeners = new CompositePersistEnumStateChangeListener();

    /**
     * Instantiates a new persist state machine handler.
     * 实例化一个新的持久化状态机Handler
     * @param stateMachine the state machine
     */
    public PersistEnumStateMachineHandler(StateMachine<S , E> stateMachine) {
        Assert.notNull(stateMachine, "State machine must be set");
        this.stateMachine = stateMachine;
    }

    @Override
    protected void onInit() throws Exception {
        stateMachine.getStateMachineAccessor().doWithAllRegions(function -> function.addStateMachineInterceptor(interceptor));
    }

    /**
     * Handle event with entity.
     * 处理entity的事件
     * @param event the event
     * @param state the state
     * @return true if event was accepted
     */
    public boolean handleEventWithState(Message<E> event, S state) {
        stateMachine.stop();
        List<StateMachineAccess<S, E>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
        for (StateMachineAccess<S, E> a : withAllRegions) {
            a.resetStateMachine(new DefaultStateMachineContext<>(state, null, null, null));
        }
        stateMachine.start();
        return stateMachine.sendEvent(event);
    }

    /**
     * Adds the persist state change listener.
     * 添加状态转换监听器
     * @param listener the listener
     */
    public void addPersistStateChangeListener(PersistEnumStateChangeListener listener) {
        listeners.register(listener);
    }

    /**
     * The listener interface for receiving persistStateChange events.
     * The class that is interested in processing a persistStateChange
     * event implements this interface, and the object created
     * with that class is registered with a component using the
     * component's <code>addPersistStateChangeListener</code> method. When
     * the persistStateChange event occurs, that object's appropriate
     * method is invoked.
     * 可以通过 addPersistStateChangeListener，增加当前Handler的PersistStateChangeListener。
     * 在状态变化的持久化触发时，会调用相应的实现了PersistStateChangeListener的Listener实例。
     */
    public interface PersistEnumStateChangeListener<S extends Enum<S>, E extends Enum<E>> {

        /**
         * Called when state needs to be persisted.
         * 当状态需要被持久化，调用此方法
         * @param state the state
         * @param message the message
         * @param transition the transition
         * @param stateMachine the state machine
         */
        void onPersist(State<S, E> state, Message<E> message, Transition<S, E> transition,
                       StateMachine<S, E> stateMachine);
    }

    private class PersistingEnumStateChangeInterceptor<S extends Enum<S>, E extends Enum<E>> extends StateMachineInterceptorAdapter<S, E> {
        /**
         * 状态预处理的拦截器方法
         * @param state
         * @param message
         * @param transition
         * @param stateMachine
         */
        @Override
        public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
            listeners.onPersist(state, message, transition, stateMachine);
        }
    }

    private class CompositePersistEnumStateChangeListener<S extends Enum<S>, E extends Enum<E>> extends AbstractCompositeListener<PersistEnumStateChangeListener<S, E>> implements
            PersistEnumStateChangeListener<S, E> {

        @Override
        public void onPersist(State<S, E> state, Message<E> message,
                              Transition<S, E> transition, StateMachine<S, E> stateMachine) {
            for (Iterator<PersistEnumStateChangeListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
                PersistEnumStateChangeListener listener = iterator.next();
                listener.onPersist(state, message, transition, stateMachine);
            }
        }
    }

}
